/*
 * Copyright (c) 2022, IMMORTA Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of IMMORTA Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef LIN_DRV_H
#define LIN_DRV_H

/*!
 * @file lin_drv.h
 * @brief This file declares lin driver interfaces
 */

/*!
 * @addtogroup lin_drv
 * @{
 */

/*******Includes***************************************************************/
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "errcodes.h"
#include "os_adapter.h"

/*******Definitions************************************************************/

/*!
 * @brief LIN mode type
 */
typedef enum {
    LIN_MODE_SLAVE      = 0U,
    LIN_MODE_MASTER     = 1U
} lin_mode_t;

/*!
 * @brief LIN parity type
 */
typedef enum {
    LIN_MAKE_PARITY     = 0U, /*!< Calculate the parity of ID */
    LIN_CHECK_PARITY    = 1U  /*!< Check the parity is correct or not */
} lin_parity_t;

/*!
 * @brief LIN bus physical state type
 */
typedef enum {
    LIN_BUS_STATE_NORMAL        = 0U, /*!< LIN bus state normal */
    LIN_BUS_STATE_SHORTED_GND   = 1U, /*!< LIN bus shorted to GND, only for RX invert not enabled */
} lin_bus_state_t;

/*!
 * @brief Defines types for an enumerating event related to an Identifier.
 */
typedef enum {
    LIN_NO_EVENT             = 0x00U,    /*!< No event yet */
    LIN_WAKEUP_SIGNAL        = 0x01U,    /*!< Received a wakeup signal */
    LIN_RECV_BREAK_FIELD_OK  = 0x02U,    /*!< Indicate that correct Break Field was received */
    LIN_SYNC_OK              = 0x03U,    /*!< Sync byte is correct */
    LIN_SYNC_ERROR           = 0x04U,    /*!< Sync byte is incorrect */
    LIN_PID_OK               = 0x05U,    /*!< PID correct */
    LIN_PID_ERROR            = 0x06U,    /*!< PID incorrect */
    LIN_FRAME_ERROR          = 0x07U,    /*!< Framing Error */
    LIN_READBACK_ERROR       = 0x08U,    /*!< Readback data is incorrect */
    LIN_CHECKSUM_ERROR       = 0x09U,    /*!< Checksum byte is incorrect */
    LIN_TX_COMPLETED         = 0x0AU,    /*!< Sending data completed */
    LIN_RX_COMPLETED         = 0x0BU,    /*!< Receiving data completed */
    LIN_RX_OVERRUN           = 0x0CU,    /*!< RX overrun flag */
    LIN_BUS_ERROR            = 0x0DU,    /*!< LIN bus shorted to GND */
    LIN_RX_STATE_ERROR       = 0x0EU     /*!< receiving date in LIN_NODE_STATE_RECV_HDR_COMPLETED state */
} lin_event_id_t;

/*!
 * @brief Define type for an enumerating LIN Node state.
 */
typedef enum {
    LIN_NODE_STATE_UNINIT               = 0x00U,    /*!< Uninitialized state */
    LIN_NODE_STATE_SLEEP                = 0x01U,    /*!< Sleep state */
    LIN_NODE_STATE_IDLE                 = 0x02U,    /*!< Idle state */
    LIN_NODE_STATE_SEND_BREAK_FIELD     = 0x03U,    /*!< Send break field state for master */
    LIN_NODE_STATE_RECV_SYNC            = 0x04U,    /*!< Receive the synchronization byte state */
    LIN_NODE_STATE_RECV_PID             = 0x05U,    /*!< Receive PID state */
    LIN_NODE_STATE_RECV_HDR_COMPLETED   = 0x06U,    /*!< Receive header completed state */
    LIN_NODE_STATE_RECV_DATA            = 0x07U,    /*!< Receive data state */
    LIN_NODE_STATE_RECV_DATA_COMPLETED  = 0x08U,    /*!< Receive data completed state */
    LIN_NODE_STATE_SEND_DATA            = 0x09U,    /*!< Send data state */
    LIN_NODE_STATE_SEND_DATA_COMPLETED  = 0x0AU     /*!< Send data completed state */
} lin_node_state_t;

/*!
 * @brief LIN Driver callback function type
 */
typedef void (* lin_callback_t)(uint32_t instance, lin_event_id_t event, void* param);

/*!
 * @brief Runtime state of the LIN driver.
 *
 * Note that the caller provides memory for the driver state structures during
 * initialization because the driver does not statically allocate memory.
 */
typedef struct {
    uint8_t cntByte;                            /*!< To count number of bytes already transmitted or received. */
    uint8_t* rxBuff;                            /*!< The buffer of received data. */
    const uint8_t* txBuff;                      /*!< The buffer of data being sent. */
    volatile uint8_t rxSize;                    /*!< The remaining number of bytes to be received. */
    volatile uint8_t txSize;                    /*!< The remaining number of bytes to be transmitted. */
    uint8_t checkSum;                           /*!< Checksum byte. */
    volatile bool isTxBusy;                     /*!< True if the LIN interface is transmitting frame data. */
    volatile bool isRxBusy;                     /*!< True if the LIN interface is receiving frame data. */
    volatile bool isBusBusy;                    /*!< True if there are data, frame headers being transferred on bus */
    volatile bool isTxBlocking;                 /*!< True if transmit is blocking transaction. */
    volatile bool isRxBlocking;                 /*!< True if receive is blocking transaction. */
    uint8_t id;                                 /*!< Current ID */
    uint8_t pid;                                /*!< Current PID */
    volatile lin_node_state_t nodeState;        /*!< Current Node state */
    volatile uint32_t timeoutCounter;           /*!< Value of the timeout counter */
    volatile bool timeoutCounterFlag;           /*!< Timeout counter flag */
    volatile bool timeoutCounterCalc;           /*!< Timeout counter calculate after autobaud */
    semaphore_t txCompleted;                    /*!< Used to wait for LIN interface ISR to complete transmission.*/
    semaphore_t rxCompleted;                    /*!< Used to wait for LIN interface ISR to complete reception*/
    lin_callback_t callback;                    /*!< Callback function to invoke after received or transmitted a byte */
    void* callbackParam;                        /*!< LIN callback parameters if necessary */
} lin_state_t;

/*!
 * @brief LIN hardware configuration structure
 */
typedef struct {
    bool        autobaud;                      /*!< Enable Autobaud feature */
    uint32_t    baudRate;                      /*!< baudrate of LIN Hardware Interface to configure */
    lin_mode_t  nodeMode;                      /*!< Node mode as Master or Slave */
    uint8_t*    classicPID;                    /*!< List of PIDs use classic checksum */
    uint8_t     numOfClassicPID;               /*!< Number of PIDs use classic checksum */
    lin_callback_t callback;                   /*!< LIN callback function */
    void* callbackParam;                       /*!< LIN callback parameters if necessary */
} lin_config_t;


/*******APIs*******************************************************************/
#if defined(__cplusplus)
extern "C" {
#endif

/*!
 * @brief Initializes an instance LIN Hardware Interface for LIN Network,
 *
 * @Note  These two point(linState,linConfig) will be reserved for asynchronously use,
 *        It must be pointed to static or global memory
 *
 * @param[in] instance:  LIN Hardware Interface instance number
 * @param[out] linState: Pointer to the LIN Hardware Interface driver state structure
 * @param[in] linConfig: User configuration structure of type #lin_config_t
 * @return operation status:
 *         - ERR_SUCCESS : Operation was successful
 *         - ERR_ERROR   : Operation failed due to semaphores initialize error
 */
errcode_t LIN_Init(uint32_t instance,
                   lin_state_t* linState,
                   lin_config_t* linConfig);

/*!
 * @brief Shuts down the LIN Hardware Interface by disabling interrupts and transmitter/receiver
 *
 * @param[in] instance: LIN Hardware Interface instance number
 * @return None
 */
void LIN_Deinit(uint32_t instance);

/*!
 * @brief Initializes a configuration structure received from the application with default values
 *
 * @param[in] isMaster:   Node function:
                - true if node is MASTER
                - false if node is SLAVE
 * @param[out] linConfig: the default configuration
 * @return None
 */
void LIN_GetDefaultConfig(bool isMaster,
                          lin_config_t* linConfig);

/*!
 * @brief Installs callback function that is used for LIN_IRQHandlerProcess
 *
 * @note  After a callback is installed, it bypasses part of the LIN Hardware Interface IRQHandler logic,
 *        Therefore, the callback needs to handle the indexes of txBuff and txSize
 *
 * @param[in] instance: LIN Hardware Interface instance number
 * @param[in] callback: the LIN receive callback function
 * @param[in] param: Param for user
 * @return None
 */
void LIN_InstallCallback(uint32_t instance,
                         lin_callback_t callback,
                         void* param);

/*!
 * @brief Sends Frame data out through the LIN Hardware Interface using blocking method
 *        This function will calculate the checksum byte and send it with the frame data,
 *        Blocking means that the function does not return until the transmission is complete
 *        This function checks if txSize is in range from 1 to 8, If not it will return ERR_ERROR
 *        This function also returns ERR_ERROR if node's current state is in SLEEP
 *        This function checks if the isBusBusy is false, if not it will return ERR_BUSY
 *        The function does not return until the transmission is complete, If the transmission
 *        is successful, it will return ERR_SUCCESS, If not, it will return ERR_TIMEOUT
 *
 *@Note   if(LIN_GetNodeState(instance) == LIN_NODE_STATE_RECV_HDR_COMPLETED) then call UART_LIN_SendFrameDataBlocking
 *
 * @param[in] instance: LIN Hardware Interface instance number
 * @param[in] txBuff:   source buffer containing 8-bit data chars to send
 * @param[in] txSize:   the number of bytes to send
 * @param[in] timeoutmSec: timeout value in milliseconds
 * @return operation status:
 *         - ERR_SUCCESS : The transmission is successful
 *         - ERR_BUSY    : The transmission is sending
 *         - ERR_TIMEOUT : The transmission isn't successful
 */
errcode_t LIN_SendFrameDataBlocking(uint32_t instance,
                                    const uint8_t* txBuff,
                                    uint8_t txSize,
                                    uint32_t timeoutmSec);

/*!
 * @brief Sends frame data out through the LIN Hardware Interface using non-blocking method
 *        This enables an a-sync method for transmitting data,Non-blocking means
 *        that the function returns immediately
 *        The application has to get the transmit status to know when the transmit is complete
 *        This function will calculate the checksum byte and send it with the frame data,
 *        The function will return immediately after calling this function
 *        If txSize is equal to 0 or greater than 8 or node's current state is in SLEEP
 *        then the function will return ERR_ERROR
 *        If isBusBusy is currently true then the function will return ERR_BUSY
 * @note  If users use LIN_TimeoutService in a timer interrupt handler, then before using this function,
 *        users have to set timeout counter to an appropriate value by using
 *        LIN_SetTimeoutCounter(instance, timeoutVal), The timeout value should be big enough to
 *        complete the transmission
 *        Timeout in real time is (timeoutVal) * (time period that LIN_TimeoutService is called),
 *        For example, if LIN_TimeoutService is called in an timer interrupt with period of 500us,
 *        then timeout in real time is timeoutValue * 500us
 * @param[in] instance: LIN Hardware Interface instance number
 * @param[in] txBuff:   source buffer containing 8-bit data chars to send
 * @param[in] txSize:   the number of bytes to send
 * @return operation status:
 *         - ERR_SUCCESS : The transmission is successful
 *         - ERR_BUSY    : Operation failed due to isBusBusy is currently true
 *         - ERR_ERROR   : Operation failed due to txSize is equal to 0 or greater than 8
 *                            or node's current state is in SLEEP
 */
errcode_t LIN_SendFrameData(uint32_t instance,
                            const uint8_t* txBuff,
                            uint8_t txSize);

/*!
 * @brief Get status of an on-going non-blocking transmission
 *        While sending frame data using non-blocking method, users can
 *        use this function to get status of that transmission
 *        The bytesRemaining shows number of bytes that still needed to transmit
 *
 * @param[in] instance:        LIN Hardware Interface instance number
 * @param[out] bytesRemaining: Number of bytes still needed to transmit
 * @return operation status:
 *         - ERR_SUCCESS : The transmission whether is successful need judge together with bytesRemaining = 0
 *         - ERR_BUSY    : The transmission is sending
 *         - ERR_TIMEOUT : Operation failed due to timeout has occurred
 */
errcode_t LIN_GetTransmitStatus(uint32_t instance,
                                uint8_t* bytesRemaining);

/*!
 * @brief Receives frame data through the LIN Hardware Interface using blocking method
 *        This function receives data from UART module using blocking method,
 *        the function does not return until the receive is complete, The interrupt handler
 *        LIN_IRQHandlerProcess will check the checksum byte:
 *        If the checksum is correct, it will receive the frame data
 *        If the checksum is incorrect, will return ERR_TIMEOUT and data in rxBuff might be wrong
 *        This function also check if rxSize is in range from 1 to 8, If not, it will return ERR_ERROR
 *        This function also returns ERR_ERROR if node's current state is in SLEEP
 *        This function checks if the isBusBusy is false, if not it will return ERR_BUSY
 *
 *@Note   if(LIN_GetNodeState(instance) == LIN_NODE_STATE_RECV_HDR_COMPLETED) then call UART_LIN_RecvFrmDataBlocking
 *
 * @param[in] instance: LIN Hardware Interface instance number
 * @param[out] rxBuff:  buffer containing 8-bit received data
 * @param[in] rxSize:   the number of bytes to receive
 * @param[in] timeoutmSec: timeout value in milliseconds
 * @return operation status:
 *         - ERR_SUCCESS : The receives frame data is successful
 *         - ERR_TIMEOUT : The checksum is incorrect.
 *         - ERR_BUSY    : Bus busy flag is true.
 *         - ERR_ERROR   : Operation failed due is equal to 0 or greater than 8 or
 *                            node's current state is in SLEEP
 */
errcode_t LIN_ReceiveFrameDataBlocking(uint32_t instance,
                                       uint8_t* rxBuff,
                                       uint8_t rxSize,
                                       uint32_t timeoutmSec);

/*!
 * @brief Receives frame data through the LIN Hardware Interface using non-blocking method
 *        This function will check the checksum byte. If the checksum is correct, it will
 *        receive it with the frame data, Non-blocking means that the function returns immediately
 *        The application has to get the receive status to know when the reception is complete
 *        The interrupt handler LIN_IRQHandlerProcess will check the checksum byte:
 *        If the checksum is correct, it will receive the frame data
 *        If the checksum is incorrect, will return ERR_TIMEOUT and data in rxBuff might be wrong
 *        This function also check if rxSize is in range from 1 to 8, If not, it will return ERR_ERROR
 *        This function also returns ERR_ERROR if node's current state is in SLEEP
 *        This function checks if the isBusBusy is false, if not it will return ERR_BUSY
 * @note  If users use LIN_TimeoutService in a timer interrupt handler, then before using this function,
 *        users have to set timeout counter to an appropriate value by using
 *        LIN_TimeoutService(instance, timeoutVal), The timeout value should be big enough to
 *        complete the reception
 *        Timeout in real time is (timeoutVal) * (time period that LIN_TimeoutService is called)
 *        For example, if LIN_TimeoutService is called in an timer interrupt with period of 500us,
 *        then timeout in real time is timeoutValue * 500us
 *
 * @param[in] instance: LIN Hardware Interface instance number
 * @param[out] rxBuff:  buffer containing 8-bit received data
 * @param[in] rxSize:   the number of bytes to receive
 * @return operation status:
 *         - ERR_SUCCESS : The receives frame data is successful
 *         - ERR_BUSY    : Bus busy flag is true.
 *         - ERR_ERROR   : Operation failed due is equal to 0 or greater than 8 or
 *                         node's current state is in SLEEP
 */
errcode_t LIN_ReceiveFrameData(uint32_t instance,
                               uint8_t* rxBuff,
                               uint8_t rxSize);

/*!
 * @brief Get status of an on-going non-blocking reception
 *        This function returns whether the data reception is complete
 *        When performing non-blocking transmit, the user can call this function to
 *        ascertain the state of the current receive progress:
 *        in progress (ERR_BUSY) or timeout (ERR_TIMEOUT) or complete (ERR_SUCCESS)
 *        In addition, if the reception is still in progress, the user can obtain the number of bytes
 *        that still needed to receive
 *
 * @param[in] instance:        LIN Hardware Interface instance number
 * @param[out] bytesRemaining: Number of bytes still needed to receive
 * @return operation status:
 *         - ERR_SUCCESS : The reception whether is complete need judge together with bytesRemaining = 0
 *         - ERR_TIMEOUT : The reception isn't complete
 *         - ERR_BUSY    : The reception is on going
 */
errcode_t LIN_GetReceiveStatus(uint32_t instance,
                               uint8_t* bytesRemaining);

/*!
 * @brief Aborts an on-going non-blocking transmission/reception
 *        While performing a non-blocking transferring data, users can call this function
 *        to terminate immediately the transferring
 *
 * @param[in] instance: LIN Hardware Interface instance number
 * @return None
 */
void LIN_AbortTransferData(uint32_t instance);

/*!
 * @brief Puts current LIN node to sleep mode
 *        This function changes current node state to LIN_NODE_STATE_SLEEP
 *
 * @param[in] instance: LIN Hardware Interface instance number
 * @return None
 */
void LIN_GotoSleepState(uint32_t instance);

/*!
 * @brief Puts current LIN node to Idle state
 *        This function changes current node state to LIN_NODE_STATE_IDLE
 *
 * @param[in] instance: LIN Hardware Interface instance number
 * @return None
 */
void LIN_GotoIdleState(uint32_t instance);

/*!
 * @brief Sends a wakeup signal through the LIN Hardware Interface
 *
 * @param instance: LIN Hardware Interface instance number
 * @return operation status:
 *         - ERR_SUCCESS : Bus busy flag is false
 *         - ERR_BUSY    : Bus busy flag is true
 */
errcode_t LIN_SendWakeupSignal(uint32_t instance);

/*!
 * @brief Get the current LIN node state
 *
 * @param[in] instance: LIN Hardware Interface instance number
 * @return current LIN node state
 */
lin_node_state_t LIN_GetNodeState(uint32_t instance);

/*!
 * @brief Callback function for Timer Interrupt Handler
 *        Users may use (optional, not required) LIN_TimeoutService to check if timeout
 *        has occurred during non-blocking frame data transmission and reception
 *        User may initialize a timer (for example TIMER/RTC) with period of 5 microseconds (recommended)
 *        In timer IRQ handler, call this function
 *
 * @param[in] instance: LIN Hardware Interface instance number
 * @return None
 */
void LIN_TimeoutService(uint32_t instance);

/*!
 * @brief Set Value for Timeout Counter that is used in LIN_TimeoutService
 *
 * @param[in] instance:   LIN Hardware Interface instance number
 * @param[in] timeoutVal: Timeout Value to be set
 * @return None
 */
void LIN_SetTimeoutCounter(uint32_t instance,
                           uint32_t timeoutVal);

/*!
 * @brief Sends frame header out through the LIN Hardware Interface using a non-blocking method
 *        This function sends LIN Break field, sync field then the ID with correct parity
 *        This function checks if the interface is Master, if not, it will return ERR_ERROR
 *        This function checks if id is in range from 0 to 0x3F, if not it will return ERR_ERROR
 *
 * @param[in] instance: LIN Hardware Interface instance number
 * @param[in] ID:       Frame Identifier
 * @return operation status:
 *         - ERR_SUCCESS : The transmission is successful
 *         - ERR_BUSY    : Bus busy flag is true.
 *         - ERR_ERROR   : The interface isn't Master or id isn't in range from 0 to 0x3F
 *                         or node's current state is in SLEEP
 */
errcode_t LIN_MasterSendHeader(uint32_t instance,
                               uint8_t ID);

/*!
 * @brief Set LIN hardware interrupts to enable or disable
 *
 * @param[in] instance: LIN Hardware Interface instance number
 * @param[in] enable: Enable or disable the LIN interrupt
 *            - true: Enable the LIN interrupt
 *            - false: Disable the LIN interrupt
 * @return None
 */
void LIN_SetIntEnable(uint32_t instance, bool enable);

/*!
 * @brief Gets the bus external physical state
 *
 * @param[in] instance: LIN Hardware Interface instance number
 * @return Bus error status.
 *       -LIN_BUS_STATE_NORMAL: bus normal
 *       -LIN_BUS_STATE_SHORTED_GND: bus error, shorted to GND
 */
lin_bus_state_t LIN_GetBusPhyState(uint32_t instance);

/*!
 * @brief Makes or checks parity bits. If action is checking parity, the function
 *        returns ID value if parity bits are correct or 0xFF if parity bits are incorrect
 *        If action is making parity bits, then from input value of ID, the function returns PID
 *        This is not a public API as it is called by other API functions
 *
 * @param[in] PID: PID byte in case of checking parity bits or ID byte in case of making parity bits
 * @param[in] typeAction: 1 for Checking parity bits, 0 for making parity bits
 * @return Value has 8 bit:
 *         - 0xFF : Parity bits are incorrect,
 *         - ID   : Checking parity bits are correct
 *         - PID  : typeAction is making parity bits
 */
uint8_t LIN_ProcessParity(uint8_t PID,
                          lin_parity_t typeAction);

/*!
 * @brief Makes the checksum byte for a frame. For PID of identifiers,
 *        if PID is 0x3C (ID 0x3C) or 0x7D (ID 0x3D) or 0xFE (ID 0x3E) or 0xBF (ID 0x3F)
 *        apply classic checksum and apply enhanced checksum for other PID
 *        In case user want to calculate classic checksum please set PID to zero
 *
 * @param[in] buffer:  Pointer to Tx buffer
 * @param[in] dataLen: Number of bytes that are contained in the buffer
 * @param[in] PID:     Protected Identifier byte
 * @return the checksum byte
 */
uint8_t LIN_MakeChecksumByte(const uint8_t* buffer,
                             uint8_t dataLen,
                             uint8_t PID);

#if defined(__cplusplus)
}
#endif

/*! @} */

#endif /* LIN_DRV_H */

/*******EOF********************************************************************/
